/* CSci4061 F2012 Assignment 2
* section: 4
* section: 3
* date: 10/29/12
* name: Carlos Cristobal, Joseph Selvik
* id: 4249701, 4000301 */


#include "wrapper.h"
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
extern int errno;


/* 
 * Name:		uri_entered_cb
 * Input arguments:	'entry'-address bar where the url was entered
 *			'data'-auxiliary data sent along with the event
 * Output arguments:	void
 * Function:		When the user hits the enter after entering the url
 *			in the address bar, 'activate' event is generated
 *			for the Widget Entry, for which 'uri_entered_cb'
 *			callback is called. Controller-tab captures this event
 *			and sends the browsing request to the router(/parent)
 *			process.
 */
void uri_entered_cb(GtkWidget* entry, gpointer data)
{
	if(!data)
		return;

	// Create a new browser_window
	browser_window* b_window = (browser_window*)data;
	comm_channel comm_channel = (b_window)->channel;

	// Get the tab index where the URL is to be rendered
	int tab_index = query_tab_id_for_request(entry, data);
	if(tab_index <= 0 || tab_index > 10)
	{
		// Fill in your error handling code here.
		perror("uri_entered_cb: tab_index error");
	}

	// Get the URL.
	char* uri = get_entered_uri(entry);
	
	// Prepare 'request' packet to send to router (/parent) process.
	child_req_to_parent req;

	// enter req.type
	req.type = NEW_URI_ENTERED;
	
	// fill in 'render in tab' field
	req.req.uri_req.render_in_tab = tab_index;
	
	// fill in 'uri' field
	strcpy(req.req.uri_req.uri, uri);

	// Send the request through the proper FD.
	// Send from Controller to Router (parent)
	write(comm_channel.child_to_parent_fd[1], &req, sizeof(req));

//***********************************************************	//for testing:
/*	printf("req_type: %d\n", req.type);
	printf("render_in_tab: %d\n", req.req.uri_req.render_in_tab);
	printf("uri: %s\n", req.req.uri_req.uri);
*/
//***********************************************************	

}//end uri_entered_cb



/*
 * Name:		wait_for_browsing_req
 * Input Arguments:	fds[] - file descriptor on which
 *			'ordinary' child tab listen for request
 *			from 'controller' tab to render web pages.
 * Output arguments:    0-sucess
 *			-1- otherwise
 * Function:		The 'ordinary' child-tabs processes perform
 *			two tasks at tandem.
 *			1). Process GTK+ window-related events
 *			2). Listen for URL rendering requests from
 *			    'controller' tab.
 */
int wait_for_browsing_req(int fds[2], browser_window *b_window)
{
	// Set up for non-blocking IO
	// Set up flags for fcntl so we can change some file descriptors
	int flags = fcntl(fds[0], F_GETFL, 0);
	if(fcntl (fds[0], F_SETFL, flags | O_NONBLOCK) == -1)
	{
		perror("wait for browsing req setting up non blocking error");
		exit(-1);
	}

	//initialize request to read from pipe
	child_req_to_parent req;

	// Continuous loop of checking requests and processing browser events
	while(1)
	{
		usleep(5000);

		// read incoming requests
		int nread = read(fds[0], &req, sizeof(req));
		
		// If read received no data && errno == EAGAIN - just process any pending events and move along.
		if (nread < 0 && errno == EAGAIN)
		{
			// No browsing request from 'controller' tab
			// Hence process GTK+ window related function (non-blocking).
			process_single_gtk_event();
		}

		//We have data: NEW_URI_ENTERED or TAB_KILLED
		else if(nread > 0)
		{
			// Handle all request types of CREATE_TAB, NEW_URI_ENTERED, 
			// and TAB_KILLED; for example, with a switch.
			//req_type req_type = req.type;
			switch (req.type)
			{
				case NEW_URI_ENTERED:
					render_web_page_in_tab(req.req.uri_req.uri, b_window);
					break;
				case TAB_KILLED:
					process_all_gtk_events();
					close((b_window)->channel.parent_to_child_fd[0]);
					close((b_window)->channel.child_to_parent_fd[1]);
					return 0;
				default:
					perror("Switch error");
					break;
			}

		}//end else if

	}//end while(1)

	return 0;

}//end wait_for_browsing_req


/*
 * Name:		wait_for_child_reqs
 * Input Arguments:	'total_tabs': Number of tabs created so far
 * Output arguments:  	0 : Success
 *			-1: Otherwise
 * Function:		Router (/parent) process listens for request from
 *			'controller' tab and performs the required 
 *			functionality based on request code.
 */
int wait_for_child_reqs(comm_channel* channel, int tab_index)
{
	//This is the router loop, needs to get pids, and all channels
	//initialize read space and requests
	int nread;
	child_req_to_parent req;
	

	//initialize alive array, alive[0] is always the controller tab and alive (==1)
	int alive[UNRECLAIMED_TAB_COUNTER];
	int j;
	for(j = 0; j < UNRECLAIMED_TAB_COUNTER; j++)
	{
		if(j == 0)
		{
			alive[j] = 1;
		}
		else
		{
			alive[j] = 0;
		}
	}


	// Listen for child requests 
	while(1)
	{
		// Sleep for 0.5 sec so we don't waste CPU time
		usleep(500000);

		// Poll (read) all tabs that exist.
		// This will handle CREATE_TAB, NEW_URI_ENTERED, and TAB_KILLED.
		int i;
		for(i = 0; i < UNRECLAIMED_TAB_COUNTER; i++){

			//if child is alive, check it
			if(alive[i] == 1)
			{

				// Set the read FD to noblock for all tabs; remember to check for error returns!
				int flags = fcntl(channel[i].child_to_parent_fd[0], F_GETFL, 0);
				if(fcntl (channel[i].child_to_parent_fd[0], F_SETFL, flags | O_NONBLOCK) == -1)
				{
					perror("Wait fo child req setting up non blocking error");
					exit(-1);
				}

				//read the child's pipe
				nread = read(channel[i].child_to_parent_fd[0], &req, sizeof(req));

				//received a command from child's pipe: process now
				if(nread > 0)
				{
					int l;

//******************************************************************************
/* 					print pre alive array: for debugging

					for(l = 0; l < UNRECLAIMED_TAB_COUNTER; l++)
					{
						printf("Pre Alive[%d] st: %d\n", l, alive[l]);
					}
*/	
//********************************************************************************				

					if(req.type == CREATE_TAB)
					{
						//find first dead tab in list
						int new_tab_index = -1;
						for(l = 1; l < UNRECLAIMED_TAB_COUNTER; l++)
						{
							if(alive[l] == 0)
							{
								new_tab_index = l;
								break;
							}
						}

						if(new_tab_index == -1)
						{
							perror("Too many tabs");
							break;
						}

						//create new tab in this index
						if(create_proc_for_new_tab(channel, new_tab_index) == -1)
						{
							perror("failed create proc");
							break;
						}

						tab_index++;
						alive[new_tab_index] = 1;
					}//end create tab request


					if(req.type == NEW_URI_ENTERED)
					{
						//bounce to child
						if(alive[req.req.uri_req.render_in_tab] == 0 || req.req.uri_req.render_in_tab < 1 || req.req.uri_req.render_in_tab > UNRECLAIMED_TAB_COUNTER-1)
						{
							perror("Invalid tab");
							break;
						}
						write(channel[req.req.uri_req.render_in_tab].parent_to_child_fd[1], &req, sizeof(req));
					}//end uri entered request


					if(req.type == TAB_KILLED)
					{
						//message contains tab index of killed tab
						//tell url child to die
						write(channel[req.req.killed_req.tab_index].parent_to_child_fd[1], &req, sizeof(req));

						//wait on pid, req.req.new_tab_req.tab_index
						waitpid(channel[req.req.killed_req.tab_index].pid, NULL, 0);

						//close fds of comm channel
						close(channel[req.req.killed_req.tab_index].parent_to_child_fd[1]);	//ptoc write
						close(channel[req.req.killed_req.tab_index].child_to_parent_fd[0]);	//ctop read

						//update alive
						alive[req.req.killed_req.tab_index] = 0;
					}//end tab killed request


					//if controller is dead, close all tabs and return
					if(alive[0] == 0)
					{
//******************************************* printf("Controller closed. All open tabs closed.\n");
						for(l=1; l < UNRECLAIMED_TAB_COUNTER; l++)
						{
							if(alive[l] == 1)
							{
								write(channel[l].parent_to_child_fd[1], &req, sizeof(req));

								//wait on pid, req.req.new_tab_req.tab_index
								waitpid(channel[l].pid, NULL, 0);

								//close fds of comm channel
								close(channel[l].parent_to_child_fd[1]);	//ptoc write
								close(channel[l].child_to_parent_fd[0]);	//ctop read

								//update alive
								alive[l] = 0;
							}
						}
						// Close final read-end pipe to child 
						waitpid(channel[0].pid, NULL, 0);
						close(channel[0].child_to_parent_fd[0]);
						return 0;
					}//end controller is dead


//*************************************************************************   //print post alive array: for debugging
/*					for(l = 0; l < UNRECLAIMED_TAB_COUNTER; l++)
					{
						printf("Post Alive[%d] st: %d\n", l, alive[l]);
					}
*/
//**************************************************************************************************************
				}//end request processing

			}//end if(alive[i] == 1) 

		}//end for polling

	}//end while(1)

	return 0;
}//end wait_for_child_reqs



/*
 * Name:		new_tab_created_cb
 * Input arguments:	'button' - whose click generated this callback
 *			'data' - auxillary data passed along for handling
 *			this event.
 * Output arguments:    void
 * Function:		This is the callback function for the 'create_new_tab'
 *			event which is generated when the user clicks the '+'
 *			button in the controller-tab. The controller-tab
 *			redirects the request to the parent (/router) process
 *			which then creates a new child process for creating
 *			and managing this new tab.
 * 
 * We also refer to this as "create_new_tab_cb" in Appendix C.
*/
void new_tab_created_cb(GtkButton *button, gpointer data)
{
//*******************************************	printf("PID of this process = %x.\nNew tab created. \n", getpid());
	if(!data)
		return;
	browser_window* b_window = ((browser_window*)data);
	comm_channel channel = (b_window)->channel;

	// Create new request.
	child_req_to_parent new_req;

	// Populate it with request type, CREATE_TAB, and tab index pulled from (browser_window*) data
	new_req.type = CREATE_TAB;

	//fill with index number for specific tab
	new_req.req.new_tab_req.tab_index = b_window->tab_index;
	
	//Send the request to the parent (/router) process through the proper FD.
	write(channel.child_to_parent_fd[1], &new_req, sizeof(new_req));

}//end new_tab_created


/*
 * Name: 		create_proc_for_new_tab
 * Input arguments: 	tab_index: Tab index for which
 *			new child process is to be created.
 * Output arguments: 	0 -success
 *			-1-otherwise
 * Function:		Creates child process for managing
 *			new tab and establishes pipes for
 *			bi-directional communication.
 */
int create_proc_for_new_tab(comm_channel* channel, int tab_index)
{
	// Create bi-directional pipes (hence 2 pipes) for 
	// communication between the parent and child process.
	// Remember to error check.
	if (pipe(channel[tab_index].parent_to_child_fd) || pipe(channel[tab_index].child_to_parent_fd) == -1)
	{
		perror("Pipe error");
		exit(-1);
	}


	// Create child process for managing the new tab; remember to check for errors!
	// The first new tab is CONTROLLER, but the rest are URL-RENDERING type.
	pid_t childPid;
	if((childPid = fork()) == -1)
	{
		perror("Fork failed");
		exit(-1);
	}

	//Child process***************
	if (childPid == 0)
	{
		// Child process should close unused pipes and launch
		// a window for either CONTROLLER or URL-RENDERING tabs.

		browser_window *b_window = NULL;
		channel[tab_index].pid = getpid();

		// Controller tab
		if(tab_index == 0)
		{
			// Controller only needs to write to the router
			close(channel[tab_index].child_to_parent_fd[0]);	// read ctop
			close(channel[tab_index].parent_to_child_fd[1]);	// write ptoc
			close(channel[tab_index].parent_to_child_fd[0]);	// read ptoc

			// Create the 'controller' tab
			create_browser(CONTROLLER_TAB, 
				tab_index,
				G_CALLBACK(new_tab_created_cb), 
				G_CALLBACK(uri_entered_cb), 
				&b_window,
				channel[tab_index]);


			// Display the 'controller' tab window.
			// Loop for events
			show_browser();
			// Close last pipe
			process_all_gtk_events();
			close(channel[0].child_to_parent_fd[1]);

		}// end controller tab


		// URL tab
		else
		{
			// Close unused pipes
			close(channel[tab_index].child_to_parent_fd[0]);	//read ctop
			close(channel[tab_index].parent_to_child_fd[1]);	//write ptoc

			// Create the 'url' tabs.
			create_browser(URL_RENDERING_TAB,
				tab_index,
				G_CALLBACK(new_tab_created_cb),
				G_CALLBACK(uri_entered_cb),
				&b_window,
				channel[tab_index]);


			// Wait for the browsing requests.
			// User enters the url on the 'controller' tab
			// which redirects the request to appropriate
			// child tab via the parent-tab.
			wait_for_browsing_req(channel[tab_index].parent_to_child_fd, b_window);

		}// End URL tab

		exit(0);

	}//end child process


	//Parent Process**************************
	else if(childPid > 0)
	{
		// Parent Process: close proper FDs and start 
		// waiting for requests if the tab index is 0.
		channel[tab_index].pid = getpid();

		//controller tab
		if(tab_index == 0)
		{
			close(channel[tab_index].child_to_parent_fd[1]);	//write ctop
			close(channel[tab_index].parent_to_child_fd[1]);	//write ptoc
			close(channel[tab_index].parent_to_child_fd[0]);	//read ptoc

			wait_for_child_reqs(channel, 1);	//goes into a while(1) loop
		}

		//url tab
		else
		{
			close(channel[tab_index].child_to_parent_fd[1]);	//write ctop
			close(channel[tab_index].parent_to_child_fd[0]);	//read ptoc
		}

		return 0;

	}//end Parent Process

	return 0;

}//end create_proc_for_new_tab



/*  The Main function doesn't need to do much -
* create a comm_channel and create process for a
* new tab.  This first tab will become the main
* controller tab.  As always, you should modularize
* your code and separate functionality by making new
* functions responsible for such things as
* waiting for a browsing request, waiting for child requests,
* or create a process for a new tab.  We have provided
* suggestions and signatures but you are free to modify the 
* given code, as long as it still works.
*/
int main( int argc, const char* argv[] )
{
	if (argc > 1)
	{
		printf("Incorrect syntax: browser takes no arguments.\n");
		return -1;
	}
	else
	{	
		comm_channel ccList[10];		//list of all comm channels
		create_proc_for_new_tab(ccList, 0);	//insert reference for pointer argument

		return 0;
	}
}//end main

